// this the core of the internal storage management
import { CREDENTIAL_STORAGE_KEY } from 'jsonql-constants'
import { isObject, isArray } from 'jsonql-params-validator'
import { JsonqlValidationError } from 'jsonql-errors'
// chaining into the classes
import { localStore, sessionStore } from '../stores'
import { timestamp, inArray, ENDPOINT_TABLE, USERDATA_TABLE } from '../utils'

import AuthCls from './auth-cls'

// This class will only focus on the storage system
export default class JsonqlBaseClient extends AuthCls {

  constructor(opts, Fly = null) {
    if (Fly) {
      opts.Fly = Fly;
    }
    super(opts)
  }

  // @TODO
  set storeIt(args) {
    console.info('storeIt', args)
    // the args MUST contain [0] the key , [1] the content [2] optional expired in
    if (isArray(args) && args.length >= 2) {
      Reflect.apply(localStore.set, localStore, args)
    }
    throw new JsonqlValidationError(`Expect argument to be array and least 2 items!`)
  }

  // this table index key will drive the contract
  // also it should not allow to change dynamicly
  // because this is how different client can id itself
  // OK this could be self manage because when init a new client
  // it will add a new endpoint and we will know if they are the same or not
  // but the check here
  set jsonqlEndpoint(endpoint) {
    let urls = localStore.get(ENDPOINT_TABLE) || []
    // should check if this url already existed?
    if (!inArray(urls, endpoint)) {
      urls.push(endpoint)
      this.storeId = [ENDPOINT_TABLE, urls]
      this[ENDPOINT_TABLE + 'Index'] = urls.length - 1;
    }
  }

  // by the time it call the save contract already been checked
  set jsonqlContract(args) {
    const key = this.opts.storageKey;
    let _args = [ key ]
    let [ contract, expired ] = args;
    // get the endpoint index
    let contracts = localStore.get(key) || []
    contracts[ this[ENDPOINT_TABLE + 'Index'] || 0 ] = contract;
    _args.push(contracts)
    if (expired) {
      _args.push(expired)
    }
    if (this.opts.keepContract) {
      this.storeIt = _args;
    }
  }

  /**
   * save token
   * @param {string} token to store
   * @return {string|boolean} false on failed
   */
  set jsonqlToken(token) {
    const key = CREDENTIAL_STORAGE_KEY;
    let tokens = localStorage.get(key) || []
    if (!inArray(tokens, token)) {
      let index = tokens.length - 1;
      tokens[ index ] = token;
      this[key + 'Index'] = index;
      let args = [key, tokens]
      if (this.opts.tokenExpired) {
        const expired = parseFloat(this.opts.tokenExpired)
        if (!isNaN(expired) && expired > 0) {
          const ts = timestamp()
          args.push( ts + parseFloat(expired) )
        }
      }
      this.storeIt = args;
      // now decode it and store in the userdata
      this.jsonqlUserdata = this.decoder(token)
      return token;
    }
    return false;
  }

  /**
   * this one will use the sessionStore
   * basically we hook this onto the token store and decode it to store here
   */
  set jsonqlUserdata(userdata) {
    const args = [USERDATA_TABLE, userdata]
    if (userdata.exp) {
      args.push(userdata.exp)
    }
    return Reflect.apply(localStore.set, localStore, args)
  }

  /**
   * This also manage the index internally
   * There is NO need to store them in an array
   * because each instance contain one end point
   * @return {string} the end point to call
   */
  get jsonqlEndpoint() {
    let urls = localStore.get(ENDPOINT_TABLE)
    if (!urls) {
      const { hostname, jsonqlPath } = this.opts;
      let url = [hostname, jsonqlPath].join('/')
      this.jsonqlEndpoint = url;
      return url;
    }
    return urls[this[ENDPOINT_TABLE + 'Index']]
  }

  /**
   * If already stored then return it by the end point index
   * or false when there is none
   * 1.2.0 start using the keepContract option (replace the useLocalStorage)
   * @return {object|boolean} as described above
   */
  get jsonqlContract() {
    const key = this.opts.storageKey
    let contracts = localStore.get(key) || []
    return contracts[ this[ENDPOINT_TABLE + 'Index'] ] || false
  }

  /**
   * Jsonql token getter
   * @return {string|boolean} false when failed
   */
  get jsonqlToken() {
    const key = CREDENTIAL_STORAGE_KEY;
    let tokens = localStorage.get(key)
    if (tokens) {
      return tokens[ this[key + 'Index'] ]
    }
    return false;
  }

  /**
   * this one store in the session store
   * get login userdata decoded jwt
   * @return {object|null}
   */
  get jsonqlUserdata() {
    return sessionStore.get(USERDATA_TABLE)
  }

  /**
   * simple log
   */
  log(...args) {
    if (this.opts.debugOn === true) {
      Reflect.apply(console.info, console, args)
    }
  }

}
